/*******************************************************************************
 * Copyleft (c) 2021 Kcode
 *
 * @file    cmos_ov7740_drv.c
 * @brief   cmos_0v7740摄像头的驱动文件
 * @author  K
 * @version 0.0.1
 * @date    2021-07-26
 * @license MulanPSL-1.0
 *
 * 文件修改历史：
 * <时间>       | <版本>    | <作者>  | <描述>
 * 2021-07-31   | v0.0.1    | Kcode   | cmos_0v7740摄像头的驱动文件
 * -----------------------------------------------------------------------------
 ******************************************************************************/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <linux/mm.h>
#include <asm/atomic.h>
#include <asm/unaligned.h>

#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf-core.h>

#include <linux/clk.h>
#include <asm/io.h>


#define CAM_SRC_HSIZE       (640) /**< 摄像头水平分辨率 */
#define CAM_SRC_VSIZE       (480) /**< 摄像头垂直分辨率 */

/* 摄像头颜色顺序 */
#define CAM_ORDER_YCbYCr    (0)
#define CAM_ORDER_YCrYCb    (1)
#define CAM_ORDER_CbYCrY    (2)
#define CAM_ORDER_CrYCbY    (3)

#define WinHorOfst		    (0) /**< 水平方向裁剪大小 */
#define WinVerOfst		    (0) /**< 垂直方向裁剪大小 */

/*!
 * 描述所分配用于CAMIF的缓冲区
 */
typedef struct camif_buffer {
    unsigned int order;         /**< 缓冲区大小 */
    unsigned long virt_base;    /**< 缓冲区虚拟基地址 */
    unsigned long phy_base;     /**< 缓冲区物理基地址 */
}CAMIF_BUFFER_T;

/*!
 * 所需要分配的4个缓冲区的描述数据
 */
static CAMIF_BUFFER_T img_buff[] = {
    {
        .order = 0,
        .virt_base = (unsigned long)NULL,
        .phy_base = (unsigned long)NULL,
    },
    {
        .order = 0,
        .virt_base = (unsigned long)NULL,
        .phy_base = (unsigned long)NULL,
    },
    {
        .order = 0,
        .virt_base = (unsigned long)NULL,
        .phy_base = (unsigned long)NULL,
    },
    {
        .order = 0,
        .virt_base = (unsigned long)NULL,
        .phy_base = (unsigned long)NULL,
    },
};

/*!
 * 描述cmos_ov7740的I2C相关设置
 */
typedef struct cmos_ov7740_i2c_value {
    unsigned char regaddr;  /**< 寄存器地址 */
    unsigned char value;    /**< 寄存器的值 */
}OV7740_I2C_T;

/*!
 * 根据原厂提供的I2C初始化数组进行设置
 */
OV7740_I2C_T ov7740_setting_30fps_VGA_640_480[] =
{
	{0x12, 0x80},
	{0x47, 0x02},
	{0x17, 0x27},
	{0x04, 0x40},
	{0x1B, 0x81},
	{0x29, 0x17},
	{0x5F, 0x03},
	{0x3A, 0x09},
	{0x33, 0x44},
	{0x68, 0x1A},

	{0x14, 0x38},
	{0x5F, 0x04},
	{0x64, 0x00},
	{0x67, 0x90},
	{0x27, 0x80},
	{0x45, 0x41},
	{0x4B, 0x40},
	{0x36, 0x2f},
	{0x11, 0x01},
	{0x36, 0x3f},
	{0x0c, 0x12},

	{0x12, 0x00},
	{0x17, 0x25},
	{0x18, 0xa0},
	{0x1a, 0xf0},
	{0x31, 0xa0},
	{0x32, 0xf0},

	{0x85, 0x08},
	{0x86, 0x02},
	{0x87, 0x01},
	{0xd5, 0x10},
	{0x0d, 0x34},
	{0x19, 0x03},
	{0x2b, 0xf8},
	{0x2c, 0x01},

	{0x53, 0x00},
	{0x89, 0x30},
	{0x8d, 0x30},
	{0x8f, 0x85},
	{0x93, 0x30},
	{0x95, 0x85},
	{0x99, 0x30},
	{0x9b, 0x85},

	{0xac, 0x6E},
	{0xbe, 0xff},
	{0xbf, 0x00},
	{0x38, 0x14},
	{0xe9, 0x00},
	{0x3D, 0x08},
	{0x3E, 0x80},
	{0x3F, 0x40},
	{0x40, 0x7F},
	{0x41, 0x6A},
	{0x42, 0x29},
	{0x49, 0x64},
	{0x4A, 0xA1},
	{0x4E, 0x13},
	{0x4D, 0x50},
	{0x44, 0x58},
	{0x4C, 0x1A},
	{0x4E, 0x14},
	{0x38, 0x11},
	{0x84, 0x70}
};

#define OV7740_INIT_REGS_SIZE \
    (sizeof(ov7740_setting_30fps_VGA_640_480) / \
        sizeof(ov7740_setting_30fps_VGA_640_480[0]))

/*!
 * 描述所支持颜色格式的结构体
 */
typedef struct cmos_ov7740_fmt {
	char  *name;    /**< 格式名字 */
	u32   fourcc;   /**< 格式id */
	int   depth;    /**< 颜色深度 */
}CMOS_OV7740_FMT;

/*!
 * cmos_ov7740设备所支持的颜色格式
 */
static CMOS_OV7740_FMT s_cmos_ov7740_formats[] = {
    {
        .name     = "RGB565",
        .fourcc   = V4L2_PIX_FMT_RGB565,
        .depth    = 16,
    },
    {
        .name     = "PACKED_RGB_888",
        .fourcc   = V4L2_PIX_FMT_RGB24,
        .depth    = 24,
    },
};

/*!
 * 描述cmos_ov7740摄像头的压缩信息
 */
typedef struct cmos_ov7740_scaler {
    unsigned int PreHorRatio;       /**< 预览缩放的水平比 */
    unsigned int PreVerRatio;       /**< 预览缩放的垂直比 */
    unsigned int H_Shift;           /**< 水平变比 */
    unsigned int V_Shift;           /**< 垂直变比 */
    unsigned int PreDst_Width;      /**< 预览目标宽度 */
    unsigned int PreDst_Height;     /**< 预览目标高度 */
    unsigned int MainHorRatio;      /**< 主缩放水平比 */
    unsigned int MainVerRatio;      /**< 主缩放垂直比 */
    unsigned int SHfactor;          /**< 缩放变比 */
    unsigned int ScaleUpDown;       /**< 放大/缩小 */
}CMOS_OV7740_SCALER;

static CMOS_OV7740_SCALER s_sc;

/* CMOS摄像头管脚相关的寄存器 */
static unsigned long *gpjcon;
static unsigned long *gpjdata;
static unsigned long *gpjup;

/* CMOS摄像头接口相关的寄存器 */
static unsigned long *cisrcfmt;
static unsigned long *cigctrl;
static unsigned long *ciwdofst;

/* 预览通道相关 */
static unsigned long *ciprclrsa1;
static unsigned long *ciprclrsa2;
static unsigned long *ciprclrsa3;
static unsigned long *ciprclrsa4;
static unsigned long *ciprtrgfmt;
static unsigned long *ciprctrl;
static unsigned long *ciprscpreratio;
static unsigned long *ciprscpredst;
static unsigned long *ciprscctrl;
static unsigned long *ciprtarea;
static unsigned long *ciimgcpt;

/* 中断相关寄存器 */
static unsigned long *srcpnd;
static unsigned long *intpnd;
static unsigned long *subsrcpnd;

static unsigned int s_SRC_Width;            /**< (裁剪后)数据源的宽度 */
static unsigned int s_SRC_Height;           /**< (裁剪后)数据源的高度 */
static unsigned int s_TargetHsize_Pr;       /**< 目标图片的垂直分辨率 */
static unsigned int s_TargetVsize_Pr;       /**< 目标图片的水平分辨率 */
static unsigned int s_bytesperline;         /**< 每行数据的字节数 */

static unsigned long s_buf_size;            /**< 分配缓冲区的大小 */

static DECLARE_WAIT_QUEUE_HEAD(cam_wait_queue); /**< 摄像头等待队列头部 */

static volatile int s_ev_cam = 0;             /**< 中断标志：0-无，1-中断 */

/* 用来挂接与适配器匹配成功的从设备i2c_client的一个链表头 */
static struct i2c_client *s_cmos_ov7740_client;

/*!
 * @brief  Step1：打开cmos_ov7740_fops设备文件
 */
static int cmos_ov7740_open(struct file *file)
{
	return 0;
}

/*!
 * @brief  Step2：查询cmos摄像头设备能力
 *         参考：uvc_v4l2_do_ioctl()
 */
static int cmos_ov7740_querycap(struct file *file, void  *priv,
					struct v4l2_capability *cap)
{	
    /*!
     * 清空内存、设置版本号和名字
     */
    memset(cap, 0, sizeof *cap);
    strcpy(cap->driver, "cmos_ov7740");
    strcpy(cap->card, "cmos_ov7740");
    cap->version = 2;

    /*!
     * V4L2_CAP_VIDEO_CAPTURE - 设备为视频捕捉设备
     * V4L2_CAP_READWRITE     - 读写方式处理视频数据
     */
    cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;

    return 0;
}

/*!
 * @brief  Step3：列举cmos摄像头设备所支持的格式format
 *         参考：uvc_fmt()
 */
static int cmos_ov7740_enum_fmt_vid_cap(struct file *file, 
							void *priv, struct v4l2_fmtdesc *f)
{
    struct cmos_ov7740_fmt *fmt;

    if (f->index >= ARRAY_SIZE(s_cmos_ov7740_formats))
        return -EINVAL;

    fmt = &s_cmos_ov7740_formats[f->index];

    strlcpy(f->description, fmt->name, sizeof(f->description));
    f->pixelformat = fmt->fourcc;

    return 0;
}

/*!
 * @brief  Step4：返回当前所使用的格式
 */
static int cmos_ov7740_g_fmt_vid_cap(struct file *file, 
							void *priv, struct v4l2_format *f)
{
	return 0;
}

/*!
 * @brief  Step5：测试驱动程序是否支持某种格式
 *         参考：uvc_v4l2_try_format()/myvivi_vidioc_try_fmt_vid_cap()
 * @return 0：为摄像头且RGB565/24，-EINVAL不支持
 */
static int cmos_ov7740_try_fmt_vid_cap(struct file *file,
							void *priv, struct v4l2_format *f)
{
    if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
            return -EINVAL;
    
    if ((f->fmt.pix.pixelformat != V4L2_PIX_FMT_RGB24) && \
                (f->fmt.pix.pixelformat != V4L2_PIX_FMT_RGB565))
            return -EINVAL;

	return 0;
}

/*!
 * @brief  Step6：设置所支持的格式
 * @return 0：成功，其他值：失败
 */
static int cmos_ov7740_s_fmt_vid_cap(struct file *file,
							void *priv, struct v4l2_format *f)
{
    int ret;
    int bpp;
    int rgb_fmt;

    /*!
     * 测试是否支持该格式
     */
    ret = cmos_ov7740_try_fmt_vid_cap(file, NULL, f);  
    if (ret < 0)
        return ret;

    /*!
     * 格式大小根据应用程序传下来的参数
     */
    s_TargetHsize_Pr = f->fmt.pix.width;
    s_TargetVsize_Pr = f->fmt.pix.height;

    rgb_fmt = ((f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) ? 1 : 0);
    *ciprscctrl &= ~(1 << 30);
    *ciprscctrl |= (rgb_fmt << 30);

    bpp = ((f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) ? 32 : 16);
    f->fmt.pix.bytesperline = (f->fmt.pix.width * bpp) >> 3;
    f->fmt.pix.sizeimage    = f->fmt.pix.height * f->fmt.pix.bytesperline;
    s_bytesperline =  f->fmt.pix.bytesperline;
    s_buf_size      = f->fmt.pix.sizeimage;

    /*!
     * CIPRTRGFMT:
     *  bit[28:16] -- 表示目标图片(最终存放在缓存中)的水平像素大小(TargetHsize_Pr)
     *  bit[15:14] -- 是否旋转，我们这个驱动就不选择了
     *  bit[12:0]  -- 表示目标图片(最终存放在缓存中)的垂直像素大小(TargetVsize_Pr)
     */
    *ciprtrgfmt = (s_TargetHsize_Pr << 16) | (0x0 << 14) | (s_TargetVsize_Pr << 0);

	return 0;
}

/*!
 * @brief  Step7：为该设备申请若干个缓冲区，由于每个buffer大小 > 128Kb，
 *         因此调用__get_free_pages获得虚拟地址，随后得到物理地址并存储到预览模式的寄存器
 * @return 0：成功，-ENOMEM：失败
 */
static int cmos_ov7740_reqbufs(struct file *file,
						void *priv, struct v4l2_requestbuffers *p)
{
    int i;
    unsigned int order;
    
    order = get_order(s_buf_size);

    for (i = 0; i < 4; i++) {
        img_buff[i].order = order;
        
        img_buff[i].virt_base = __get_free_pages(GFP_KERNEL | ___GFP_DMA, img_buff[i].order);
        if (img_buff[i].virt_base == (unsigned long)NULL)
            goto error;

        img_buff[i].phy_base  = __virt_to_phys(img_buff[i].virt_base);
    }
    
    *ciprclrsa1 = img_buff[0].phy_base;
    *ciprclrsa2 = img_buff[1].phy_base;
    *ciprclrsa3 = img_buff[2].phy_base;
    *ciprclrsa4 = img_buff[3].phy_base;
    
	return 0;

error:
    for (i -= 1; i >= 0; i--) {
        free_pages(img_buff[i].virt_base, order);
        img_buff[i].phy_base = (unsigned long)NULL; 
    }
    
    return -ENOMEM;
}
                    
/*!
 * @brief  计算预览模式下DMA处理数据的主突发长度和剩余突发长度
 *         出于速度的考虑，只考虑burst lengths : 4, 8, 16.
 * @return 无
 */
static void CalculateBurstSize(unsigned int bytesperline, 
            unsigned int *main_burst, unsigned int *remained_burst)
{
    unsigned int tmp;

    tmp = (bytesperline / 4) % 16;
    switch(tmp) {
	case 0:
		*main_burst = 16;
		*remained_burst = 16;
		break;
        
	case 4:
		*main_burst = 16;
		*remained_burst = 4;
		break;
        
	case 8:
		*main_burst = 16;
		*remained_burst = 8;
		break;
        
	default:
		tmp = (bytesperline / 4) % 8;
        
		switch(tmp) {
		case 0:
			*main_burst = 8;
			*remained_burst = 8;
			break;
            
		case 4:
			*main_burst = 8;
			*remained_burst = 4;
			break;
            
		default:
			*main_burst = 4;
			tmp = (bytesperline / 4) % 4;
			*remained_burst = ((tmp) ? tmp : 4);
			break;
        }            
		break;
    }
        
}

/*!
 * @brief  获得摄像头接口的缩放系数
 * @return 无
 */
static void camif_get_scaler_factor(unsigned int src, unsigned int tar, 
                                unsigned int *ratio, unsigned int *shift)
{
    if (src >= (64 * tar)) { return ; /* Out Of Horizontal Scale Range */ }
    else if (src >= (32 * tar)) { *ratio = 32; *shift = 5; }
    else if (src >= (16 * tar)) { *ratio = 16; *shift = 4; }
    else if (src >= (8  * tar)) { *ratio = 8;  *shift = 3; }
    else if (src >= (4  * tar)) { *ratio = 4;  *shift = 2; }
    else if (src >= (2  * tar)) { *ratio = 2;  *shift = 1; }
    else { *ratio = 1; *shift = 0; }
}

/*!
 * @brief  计算预览模式下缩放信息
 * @return 无
 */
static void cmos_ov7740_calculate_scaler_info(void)
{
    unsigned int sx, sy;
    unsigned int tx, ty;

    sx = s_SRC_Width;
    sy = s_SRC_Height;
    tx = s_TargetHsize_Pr;
    ty = s_TargetVsize_Pr;

    printk("%s SRC_in(%d, %d),Target_out(%d, %d)\n", __func__, sx, sy, tx, ty);

    camif_get_scaler_factor(sx, tx, &s_sc.PreHorRatio, &s_sc.H_Shift);
    camif_get_scaler_factor(sy, ty, &s_sc.PreVerRatio, &s_sc.V_Shift);

    s_sc.PreDst_Width  = sx / s_sc.PreHorRatio;
    s_sc.PreDst_Height = sy / s_sc.PreVerRatio;

    s_sc.MainHorRatio  = (sx << 8) / (tx << s_sc.H_Shift);    
    s_sc.MainVerRatio  = (sy << 8) / (ty << s_sc.V_Shift);

    s_sc.SHfactor = 10 - (s_sc.H_Shift + s_sc.V_Shift);
    s_sc.ScaleUpDown = (tx >= sx) ? 1 : 0;
}

/*!
 * @brief  Step8：启动数据传输
 * @return 0：成功
 */
static int cmos_ov7740_streamon(struct file *file, 
                                    void *priv, enum v4l2_buf_type t)
{
    unsigned int main_burst;
    unsigned int remained_burst;
    
    /*!
     * CISRCFMT:
     *  bit[31]    -- 选择传输方式为BT601(1)或者BT656(0)
     *  bit[30]    -- 设置偏移值(0 = +0 (正常情况下) - for YCbCr)
     *  bit[29]    -- 保留位,必须设置为0
     *  bit[28:16] -- 设置源图片的水平像素值(640)
     *  bit[15:14] -- 设置源图片的颜色顺序(0x0c(OV7740寄存器) --> 0x2)
     *  bit[12:0]  -- 设置源图片的垂直像素值(480)
     */
    *cisrcfmt |= (0 << 30) | (0 << 29) | (CAM_SRC_HSIZE << 16) | \
                  (CAM_ORDER_CbYCrY << 14) | (CAM_SRC_VSIZE << 0);

    /*！
     * CIWDOFST: (先清除溢出标志位)
     *  bit[31]        -- 1 = 使能窗口功能、0 = 不使用窗口功能
     *  bit[30、15:12] -- 清除溢出标志位
     *  bit[26:16]     -- 水平方向的裁剪的大小
     *  bit[10:0]      -- 垂直方向的裁剪的大小
     */
    *ciwdofst |= (1 << 30) | (0xf << 12);
    *ciwdofst |= (1 << 31) | (WinHorOfst << 16) | (WinVerOfst << 0);

    s_SRC_Width  = CAM_SRC_HSIZE - WinHorOfst * 2;
    s_SRC_Height = CAM_SRC_VSIZE - WinVerOfst * 2;

    /*!
	 * CIGCTRL:
	 *	bit[31]		-- 软件复位CAMIF控制器
	 *	bit[30]		-- 用于复位外部摄像头模块
	 *	bit[29]		-- 保留位，必须设置为1
	 *	bit[28:27]	-- 用于选择信号源(00 = 输入源来自摄像头模块，其他均为测试使用)
	 *	bit[26]		-- 设置像素时钟的极性(猜0)
	 *
     *  极性的确定需要对比芯片手册与具体器件的时序，一致则不需反转0，否则反转1
	 *	bit[25]		-- 设置VSYNC(帧同步信号)的极性(0)
	 *	bit[24]		-- 设置HREF(行同步信号)的极性(0)
	 */
	*cigctrl |= (1 << 29) | (0 << 27) | (0 << 26) | (0 << 25) | (0 << 24);

    /*!
	 * CIPRCTRL:
	 *  对于DMA通信，若需要传的信息为48KB，但一次性传输不了，则拆分为如下：
	 *      48KB = 16KB + 16KB + 16KB + 0KB，16KB为主突发长度，0KB为剩余突发长度
	 *	bit[23:19] -- 主突发长度(Main_burst)
	 *	bit[18:14] -- 剩余突发长度(Remained_burst)
	 *	bit[2]	   -- 是否使能LastIRQ功能(采集一帧数据后触发的中断，不使能)
	 */
	CalculateBurstSize(s_bytesperline, &main_burst, &remained_burst);
    
	*ciprctrl = (main_burst << 19) | (remained_burst << 14) | (1 << 2);

	/*!
	 * CIPRSCPRERATIO: 预览预缩放比例控制
	 *	bit[31:28] -- 预览缩放的变化系数(SHfactor_Pr)
	 *	bit[22:16] -- 预览缩放的水平比(PreHorRatio_Pr)
	 *	bit[6:0]   -- 预览缩放的垂直比(PreVerRatio_Pr)
	 *	
	 * CIPRSCPREDST: 预览预缩放目标格式
	 *	bit[27:16] -- 预览缩放的目标宽度(PreDstWidth_Pr)
	 *	bit[11:0]  -- 预览缩放的目标高度(PreDstHeight_Pr)
	 *	
	 * CIPRSCCTRL: 预览控制的主要标量
	 *	bit[29:28] -- 告诉摄像头控制器(图片是缩小、放大)(ScaleUpDown_Pr)
	 *	bit[24:16] -- 预览主缩放的水平比(MainHorRatio_Pr)
	 *	bit[8:0]   -- 预览主缩放的垂直比(MainVerRatio_Pr)
	 *	
	 *	bit[31] -- 必须固定设置为1
	 *	bit[30] -- 设置图像输出格式是RGB16、RGB24
	 *	bit[15] -- 预览缩放开始
	 */
    cmos_ov7740_calculate_scaler_info();
    *ciprscpreratio = (s_sc.SHfactor << 28) | (s_sc.PreHorRatio << 16) |\
                        (s_sc.PreVerRatio << 0);
    *ciprscpredst    = (s_sc.PreDst_Width << 16) | (s_sc.PreDst_Height << 0);
    *ciprscctrl     |= (1 << 31) | (s_sc.ScaleUpDown << 28) |\
        (s_sc.MainHorRatio << 16) | (s_sc.MainVerRatio << 0);
   
    /*!
     * CIPRTAREA: 
     *	表示预览通道的目标区域(缩放后目标图片的大小)
     */
    *ciprtarea = s_TargetHsize_Pr * s_TargetVsize_Pr;
  
    /*!
     * CIIMGCPT: 图像捕获使能控制
     *  bit[31] -- 用来使能摄像头控制器
     *  bit[30] -- 使能编码通道
     *  bit[29] -- 使能预览通道
     */
    *ciimgcpt = (1 << 31) | (1 << 29);

    *ciprscctrl |= (1 << 15);
    
    return 0;
}

/*!
 * @brief  Step0：关闭设备
 *         参考：uvc_video_enable()-->uvc_commit_video()/uvc_init_video()
 * @return 0：成功
 */
static int cmos_ov7740_streamoff(struct file *file, 
									void *priv, enum v4l2_buf_type t)
{
    *ciprscctrl &= ~(1 << 15);
    *ciimgcpt &= ~((1 << 31) | (1 << 29));

    return 0;
}
                                    
/*!
 * 分配、设置v4l2_ioctl_ops结构体
 */
static const struct v4l2_ioctl_ops s_cmos_ov7740_ioctl_ops = {
    // 表示它是一个摄像头设备
    .vidioc_querycap      = cmos_ov7740_querycap,

    /* 用于列举、获得、测试、设置摄像头的数据的格式 */
    .vidioc_enum_fmt_vid_cap  = cmos_ov7740_enum_fmt_vid_cap,
    .vidioc_g_fmt_vid_cap     = cmos_ov7740_g_fmt_vid_cap,
    .vidioc_try_fmt_vid_cap   = cmos_ov7740_try_fmt_vid_cap,
    .vidioc_s_fmt_vid_cap     = cmos_ov7740_s_fmt_vid_cap,

    /* 缓冲区操作: 申请 */
    .vidioc_reqbufs       = cmos_ov7740_reqbufs,

    /* 使用的是read方式读数据，/查询/放入队列/取出队列  操作不需要*/
    //.vidioc_querybuf      = cmos_ov7740_querybuf,
    //.vidioc_qbuf          = cmos_ov7740_qbuf,
    //.vidioc_dqbuf         = cmos_ov7740_dqbuf,

    /* 查询/获得/设置属性 */
    //.vidioc_queryctrl     = cmos_ov7740_queryctrl,
    //.vidioc_g_ctrl        = cmos_ov7740_g_ctrl,
    //.vidioc_s_ctrl        = cmos_ov7740_s_ctrl,

    /* 启动/停止 */
    .vidioc_streamon      = cmos_ov7740_streamon,
    .vidioc_streamoff     = cmos_ov7740_streamoff,   
};

/*!
 * 
 * @brief  关闭cmos_ov7740_fops设备文件
 */
static int cmos_ov7740_close(struct file *file)
{
    cmos_ov7740_streamoff(NULL, NULL, 0);
	return 0;
}

/*!
 * @brief  应用程序读出数据函数
 * @return 成功：返回实际读取到数据大小，失败：-EFAULT
 */
static ssize_t cmos_ov7740_read(struct file *file, char __user *buf, 
                                size_t count, loff_t *pos)
{
    int i;
    size_t end;

    end = min_t(size_t, s_buf_size ,count);

    /*!
     * 一开始程序休眠(可中断)
     */
    wait_event_interruptible(cam_wait_queue, s_ev_cam);
    
    /*!
     * 唤醒后把数据copy到用户空间
     */
    for (i = 0; i < 4; i++) {
        if (copy_to_user(buf, (void *)img_buff[i].virt_base, end))
            return -EFAULT;
    }

    s_ev_cam = 0;   /**< 清中断 */
    
    return end;
}

static const struct v4l2_file_operations s_cmos_ov7740_fops = {
	.owner			= THIS_MODULE,
    .open       	= cmos_ov7740_open,
    .release    	= cmos_ov7740_close,
    .unlocked_ioctl = video_ioctl2,
    .read           = cmos_ov7740_read
};

/*
 * @brief  必须的函数，否则在加载驱动时会出错
 * @return 无
 */
static void cmos_ov7740_release(struct video_device *vdev)
{
    int i;
    unsigned int order;
    
    /* 释放缓存 */
    order = get_order(s_buf_size);
    for (i = 0; i < 4; i++) {
       free_pages(img_buff[i].virt_base, order);
       img_buff[i].phy_base = (unsigned long)NULL; 
    }

}

/*!
 * 分配、设置video_device结构体
 */
static struct video_device s_cmos_ov7740_vdev = {
    .name      = "cmos_ov7740",
    .release   = cmos_ov7740_release,
    .fops      = &s_cmos_ov7740_fops,
    .ioctl_ops = &s_cmos_ov7740_ioctl_ops,
};

/*
 * @brief  设置相应的GPIO用于CMOS摄像头的CAMIF
 * @return 无
 */
static void cmos_ov7740_gpio_cfg(void)
{
    *gpjcon  = 0x2aaaaaa;    /**< 查看手册可知所有控制位设置为10 */
    *gpjdata = 0;
    *gpjup    = 0;  /**< 使能上拉电阻 */
}

/*
 * @brief  映射摄像头相关的寄存器
 * @return 无
 */
static void cmos_ov7740_reg_map(void)
{
    /* CAMERA GPIO */
    gpjcon   = ioremap(0x560000d0, 4);
    gpjdata = ioremap(0x560000d4, 4);
    gpjup    = ioremap(0x560000d8, 4);

    /* CAMERA IF */
    cisrcfmt = ioremap(0x4f000000, 4);
    ciwdofst = ioremap(0x4f000004, 4);
    cigctrl   = ioremap(0x4f000008, 4);

    /* 预览通道相关 */
    ciprclrsa1 = ioremap(0x4f00006c, 4);
    ciprclrsa2 = ioremap(0x4f000070, 4);
    ciprclrsa3 = ioremap(0x4f000074, 4);
    ciprclrsa4 = ioremap(0x4f000078, 4);
    ciprtrgfmt = ioremap(0x4f00007c, 4);
    ciprctrl    = ioremap(0x4f000080, 4);

    /* 缩放相关 */
    ciprscpreratio  = ioremap(0x4f000084, 4);
    ciprscpredst    = ioremap(0x4f000088, 4);
    ciprscctrl      = ioremap(0x4f00008c, 4);
    ciprtarea      = ioremap(0x4f000090, 4);
    ciimgcpt        = ioremap(0x4f0000a0, 4);

    /* 中断相关 */           
    srcpnd     = ioremap(0x4a000000, 4);
    intpnd     = ioremap(0x4a000010, 4);
    subsrcpnd = ioremap(0x4a000018, 4);
    
}

/*
 * @brief  取消映射摄像头相关的寄存器
 * @return 无
 */
static void cmos_ov7740_reg_unmap(void)
{
    /* CAMERA GPIO */
    iounmap(gpjcon);
    iounmap(gpjdata);
    iounmap(gpjup);
	
    /* CAMERA IF */
    iounmap(cisrcfmt);
    iounmap(ciwdofst);
    iounmap(cigctrl);
	
    /* 预览通道相关 */
    iounmap(ciprclrsa1);
    iounmap(ciprclrsa2);
    iounmap(ciprclrsa3);
    iounmap(ciprclrsa4);
    iounmap(ciprtrgfmt);
    iounmap(ciprctrl);
	
    /* 缩放相关 */
    iounmap(ciprscpreratio);
    iounmap(ciprscpredst);  
    iounmap(ciprscctrl);    
    iounmap(ciprtarea);     
    iounmap(ciimgcpt);       
	
    /* 中断相关 */
    iounmap(srcpnd);   
    iounmap(intpnd);   
    iounmap(subsrcpnd); 
}

/*
 * @brief  复位一下CAMIF的控制器
 * @return 无
 */
static void cmos_ov7740_camif_reset(void)
{
    /* 传输方式为BT601 */
    *cisrcfmt |= (1 << 31);;
    
    /* 复位 */
    *cigctrl |= (1 << 31);
    mdelay(10);

    /* 清零：正常工作 */
    *cigctrl &= ~(1 << 31);
    mdelay(10);
}

/*
 * @brief  设置、使能时钟(使能HCLK，使能并设置CAMCLK = 24MHz)
 * @return 无
 */
static void cmos_ov7740_clk_cfg(void)
{
    struct clk *camif_clk;
    struct clk *camif_upll_clk;

    /* 获取时钟"camif" */
    camif_clk = clk_get(NULL, "camif");
    if (!camif_clk || IS_ERR(camif_clk)) {
        printk(KERN_ERR"failed to get CAMIF clock source\n");
        return ;
    }

    /* 使能时钟 */
    clk_enable(camif_clk);
    
    /* 获取时钟"camif-upll" */
    camif_upll_clk = clk_get(NULL, "camif-upll");
    if (!camif_upll_clk || IS_ERR(camif_upll_clk)) {
        printk(KERN_ERR"failed to get CAMCLK clock source\n");
        return ;
    }

    /* 设置时钟CAMCLK = 24MHz */
    clk_set_rate(camif_upll_clk, 24000000);
    mdelay(50);
}

/*
 * @brief  复位摄像头，复位时序：1->0->1
 * @note   1、S3C2440提供的复位时序(CAMIF)为：0->1->0(0:正常工作的电平，1:复位电平)
 *            实验证明，该复位时序与使用的OV7740需要的复位时序(1->0->1)不符合。
 *         2、因此，需要结合OV7740的具体复位时序设置寄存器
 * @return 无
 */
static void cmos_ov7740_reset(void)
{
    *cigctrl |= (1 << 30); /**< CamRest */
    mdelay(30);

    *cigctrl &= ~(1 << 30);
    mdelay(30);

     *cigctrl |= (1 << 30);
    mdelay(30);
}

/*
 * @brief 通过IIC总线初始化摄像头模块
 * @return 无
 */
static void cmos_ov7740_init(void)
{
    int i;
    unsigned int mid;

    /* 读ID */
    mid = i2c_smbus_read_byte_data(s_cmos_ov7740_client, 0x0a) << 8;
    mid |= i2c_smbus_read_byte_data(s_cmos_ov7740_client, 0x0b);
    printk("cmos_ov7740 id :0x%x\n", mid);

    /* 写数据进行初始化 */
    for (i  = 0; i < OV7740_INIT_REGS_SIZE; i++) {
        i2c_smbus_write_byte_data(s_cmos_ov7740_client, 
                ov7740_setting_30fps_VGA_640_480[i].regaddr, 
                    ov7740_setting_30fps_VGA_640_480[i].value);
        mdelay(2);
    }
}

/*
 * @brief 摄像头编码通道中断函数(驱动无使用编码通道，不设置)
 * @return IRQ_HANDLED
 */
static irqreturn_t cmos_ov7740_camif_irq_c(int irq, void *dev_id)
{
    return IRQ_HANDLED;
}

/*
 * @brief 摄像预览通道中断函数
 * @return IRQ_HANDLED
 */
static irqreturn_t cmos_ov7740_camif_irq_p(int irq, void *dev_id)
{
    /*!
     * 清中断
     */
    *srcpnd     = (1 << 6);
    *intpnd     = (1  << 6);
    *subsrcpnd = (1 << 12);
    
    /*!
     * 唤醒休眠的等待队列
     */
    s_ev_cam = 1;
    wake_up_interruptible(&cam_wait_queue);
    
    return IRQ_HANDLED;
}

/*
 * @brief  在IIC总线设备驱动中找到对应的设备文件后就调用probe函数
 * @return  0：成功 其他值：失败
 */
static int __devinit cmos_ov7740_probe(struct i2c_client *client,
				  const struct i2c_device_id *id)
{
    int ret;

    
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

    /* 硬件相关操作 */
    /*!
     * 映射相关的寄存器
     */
    cmos_ov7740_reg_map();

    /*!
     * 设置相应的GPIO用于CAMIF
     */
    cmos_ov7740_gpio_cfg();

    /*!
     * 复位一下CAMIF的控制器
     */
    cmos_ov7740_camif_reset();

    /*!
     * 设置、使能时钟(使能HCLK，使能并设置CAMCLK = 24MHz)
     */
    cmos_ov7740_clk_cfg();

    /*!
     * 复位一下摄像头模块
     */
    cmos_ov7740_reset();

    /*!
     * 通过IIC总线初始化摄像头模块
     */
    s_cmos_ov7740_client = client;
    cmos_ov7740_init();

    /*!
     * 注册两次中断(编码通道与预览通道)：摄像头每采集一帧的数据会触发中断
     */
    if (request_irq(IRQ_S3C2440_CAM_C, cmos_ov7740_camif_irq_c, 
                                        IRQF_DISABLED, "CAM_C", NULL))
    {
        printk("%s request_irq failed\n", __func__);
        return -1;
    }

    if (request_irq(IRQ_S3C2440_CAM_P, cmos_ov7740_camif_irq_p, 
                                        IRQF_DISABLED, "CAM_P", NULL))
    {
        printk("%s request_irq failed\n", __func__);
        return -1;
    }        
    
    /*! 
     * 注册结构体，-1：自动分配次设备号
     */
    ret = video_register_device(&s_cmos_ov7740_vdev, VFL_TYPE_GRABBER, -1);
    if (ret) {
        printk("Unable to register video device (error=%i).\n", ret);
        return ret;
    }
    
	return 0;
}

/*
 * @brief  移除函数
 * @return  0：成功 -1：失败
 */
static int __devexit cmos_ov7740_remove(struct i2c_client *client)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

    cmos_ov7740_reg_unmap();

    free_irq(IRQ_S3C2440_CAM_C, NULL);
    free_irq(IRQ_S3C2440_CAM_P, NULL);
    
    video_unregister_device(&s_cmos_ov7740_vdev);
    
	return 0;
}

/*!
 * 支持设备的名字
 */
static const struct i2c_device_id s_cmos_ov7740_id_table[] = {
	{ "cmos_ov7740", 0 },
	{}
};

/*!
 * 设置i2c设备驱动结构体
 */
static struct i2c_driver s_cmos_ov7740_drv = {
	.driver	= {
		.name	= "cmos_ov7740",
		.owner	= THIS_MODULE,
	},
	.probe		= cmos_ov7740_probe,
	.remove		= __devexit_p(cmos_ov7740_remove),
	.id_table	= s_cmos_ov7740_id_table,
};

/*
 * @brief  cmos_ov7740_dev初始化函数（入口函数）
 * @return  0：成功 -1：失败
 */
static int cmos_ov7740_drv_init(void)
{
     i2c_add_driver(&s_cmos_ov7740_drv);
     return 0;
}

/*
 * @brief  cmos_ov7740_dev退出函数（出口函数）
 * @return  无
 */
static void cmos_ov7740_drv_exit(void)
{
    i2c_del_driver(&s_cmos_ov7740_drv);
}

module_init(cmos_ov7740_drv_init);
module_exit(cmos_ov7740_drv_exit);
MODULE_LICENSE("GPL");

